from datetime import datetime
from typing import List, Union

from fastapi import Query
from sqlalchemy import delete, update
from sqlalchemy.orm import Session

from constant.constant import Condition
from exception.exception import CustomException, LIST, UPLOAD, UPDATE, EXPIRED, DELETE
from model.base import Session
from model.entities import File
from model.schemas import FileVo
from service import service_minio
from service.service_minio import get_presigned_url, remove_obj
from tools.time_tool import trans_time


def update_file(uuid: str, new_name: str, old_name: str) -> str:
    """
    更新文件
    @param uuid:  uuid
    @param new_name:  新的文件名
    @param old_name:
    """
    db = Session()
    try:
        file = (db.query(File.uuid)
                .filter(File.uuid.__eq__(uuid), File.name.__eq__(old_name)).first())
        if not file:
            raise CustomException(status_code=EXPIRED.status_code, detail=EXPIRED.detail)
        else:  # 文件名已过期
            """ 修改 : 文件名、文件类型和 update_stamp """
            sql = (update(File)
                   .where(File.uuid.__eq__(uuid), File.name.__eq__(old_name), )
                   .values(name=new_name, type=new_name.split(".")[-1], update_stamp=datetime.utcnow()))
            db.execute(sql)
        db.commit()
        return str(new_name)
    except CustomException as e:  # 文件名过期，直接通过这里抛出
        print(str(e))
        raise CustomException(status_code=EXPIRED.status_code, detail=EXPIRED.detail)
    except Exception as e:
        print(str(e))
        db.rollback()
        raise CustomException(status_code=UPDATE.status_code, detail=UPDATE.detail, data=str(e))
    finally:
        db.close()


def delete_file(uuid: str, name: str, bucket: str) -> str:
    """
    删除文件名
    @param uuid: uuid
    @param name: 真实文件名
    @param bucket: 存储桶
    @return str: 被删除的文件名

    1、前端传 uuid + old_name
    2、后端加锁，修改 sql 逻辑为（where uuid=uuid & name = old_name）
    3、根据 sql 返回结果，
        ○ 如果 查到了，进行记录删除和 minio 删除（这里保证原子性），并返回删除文件名
        ○ 没查到，raise CustomerException(文件名已过期  )

    """
    db = Session()
    try:
        file = (db.query(File.uuid)
                .filter(File.uuid.__eq__(uuid), File.name.__eq__(name)).first())
        if not file:
            raise CustomException(status_code=EXPIRED.status_code, detail=EXPIRED.detail)
        else:
            sql = delete(File).where(File.uuid == file.uuid)
            db.execute(sql)
            service_minio.remove_obj(bucket_name=bucket, uuid=uuid)
            db.commit()
            return name
    except CustomException as e:  # 文件名过期，直接通过这里抛出
        print(str(e))
        raise CustomException(status_code=EXPIRED.status_code, detail=EXPIRED.detail)
    except Exception as e:
        print(str(e))
        db.rollback()
        raise CustomException(status_code=DELETE.status_code, detail=DELETE.detail, data=str(e))
    finally:
        db.close()


def list_file(current: int = 0,
              size: int = 20,
              condition: Union[str, None] = Query(default=None, title="查询条件"),
              query: Union[str, None] = Query(default=None, title="查询值")) -> List[FileVo]:
    """
    分页+条件查询 : 默认按照更新时间逆序分页，有条件加条件
    :param current: 当前页
    :param size: 查询数量
    :param condition: 查询条件
    :param query: 查询值
    :return:    List[FileVo]
    """

    db = Session()
    try:
        res_list: List[FileVo] = []
        offset = (current - 1) * size
        files = db.query(File.uuid, File.name, File.url, File.size, File.update_stamp)
        if condition is not None and query is not None and not query.__eq__(""):  # 有条件加条件，没条件直接返回分页查询结果
            if Condition.NAME.value.__contains__(condition):  # 根据文件名查询
                files = files.filter(File.name.contains(query))
            elif Condition.TYPE.value.__eq__(condition):  # 根据文件类型查询
                files = files.filter(File.type.__eq__(query))
        res = files.order_by(File.update_stamp.desc()).offset(offset).limit(size).all()
        for el in res:
            file: FileVo = FileVo(uuid=el.uuid, name=el.name, url=el.url, size=el.size,
                                  update_stamp=trans_time(el.update_stamp))
            res_list.append(file)
        db.commit()
        return res_list
    except Exception as e:
        print(str(e))
        raise CustomException(status_code=LIST.status_code, detail=LIST.detail, data=str(e))
    finally:
        db.close()


def count_file(condition: Union[str, None] = Query(default=None, title="查询条件"),
               query: Union[str, None] = Query(default=None, title="查询值")) -> int:
    """
    用于分页查询时返回 total 信息
    :param condition:
    :param query:
    :return:
    """
    db = Session()
    try:
        files = db.query(File.id)
        if condition is not None and query is not None and not query.__eq__(""):  # 有条件加条件，没条件直接返回分页查询结果
            if Condition.NAME.value.__eq__(condition):  # 根据文件名查询
                files = files.filter(File.name.contains(query))
            elif Condition.TYPE.value.__eq__(condition):  # 根据文件类型查询
                files = files.filter(File.type.__eq__(query))
        total = files.count()
        db.commit()
        return total
    except Exception as e:
        print(str(e))
        raise CustomException(status_code=LIST.status_code, detail=LIST.detail, data=str(e))
    finally:
        db.close()


def upload_file(bucket: str, uuid: str, size: int, name: str, file_type: str) -> File:
    """ 上传文件
    1、后端接收到前端的 UploadFile
    2、bucket ：全部设置为 bucket-ys
    3、生成一个 uuid 用于标识文件（上传时使用 uuid+存储桶）
    4、获取文件大小 size，file.size() 直接获取，记录到数据库
    5、文件名：从 fileupload 对象可以直接获取
    6、文件类型：文件名.spilt(".")[1]
    7、上传文件到 minio
    8、url，上传 minio 之后，获取签名，不设置过期时间
    """

    db = Session()
    try:
        url: str = get_presigned_url(bucket_name=bucket, object_name=uuid)
        obj_db = {
            "bucket": bucket,
            "uuid": uuid,
            "name": name,
            "type": file_type,
            "url": url,
            "size": size,
            # "create_time": func.now,
            # "update_time": func.now,
            # "create_stamp": datetime.utcnow,
            # "update_stamp": datetime.utcnow,
        }

        file: File = File(**obj_db)
        db.add(file)
        db.commit()
        # 这个方法的作用：它会从数据库中重新加载该对象的最新数据，并更新file对象的属性以反映数据库中的当前状态
        db.refresh(file)
        return file
    except Exception as e:
        print(str(e))
        db.rollback()
        remove_obj(bucket_name=bucket, uuid=uuid)
        raise CustomException(status_code=UPLOAD.status_code, detail=UPLOAD.detail, data=str(e))
    finally:
        db.close()


if __name__ == '__main__':
    # 获取当前系统时间
    now = datetime.now()

    # 获取当前系统时区
    local_timezone = now.astimezone().tzinfo
    print(local_timezone)
